package com.glodon.async.threadpool;

import com.alibaba.fastjson.JSONObject;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * 基于ThreadPoolExecutor实现动态线程池
 * runnableNameMap 用来保存某个线程池的运行状态，结果等
 * transactionMap 用来保存所有线程池的整体状态
 * @Author: lilin
 * @Date: 2022/8/13
 */
public class DynamicThreadPoolExecutor extends ThreadPoolExecutor {

    private String defaultTaskName = "defaultTask";

    // 默认拒绝策略
    private static final RejectedExecutionHandler rejectedExecutionHandler = new AbortPolicy();

    // 记录线程关联的所有任务
    Map<String, Map<String, String>> transactionMap  = new ConcurrentHashMap<>();
    // 每个任务执行情况
    Map<String, String> runnableNameMap = new ConcurrentHashMap<>();

    public DynamicThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public DynamicThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, rejectedExecutionHandler);
    }

    public DynamicThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public DynamicThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, rejectedExecutionHandler);
    }

    @Override
    public void execute(Runnable command) {
        runnableNameMap.put("class", command.getClass().getSimpleName());
        runnableNameMap.put("task", defaultTaskName);
        runnableNameMap.put("start_time", System.currentTimeMillis()+"");
        super.execute(command);
    }

    public void execute(Runnable command, String taskName) {
        runnableNameMap.put("class", command.getClass().getSimpleName());
        runnableNameMap.put("task", taskName);
        runnableNameMap.put("start_time", System.currentTimeMillis()+"");
        super.execute(command);
    }

    @Override
    public Future<?> submit(Runnable task) {
        runnableNameMap.put("class", task.getClass().getSimpleName());
        runnableNameMap.put("task", defaultTaskName);
        runnableNameMap.put("start_time", System.currentTimeMillis()+"");
        return super.submit(task);
    }

    public Future<?> submit(Runnable task, String taskName) {
        runnableNameMap.put("class", task.getClass().getSimpleName());
        runnableNameMap.put("task", taskName);
        runnableNameMap.put("start_time", System.currentTimeMillis()+"");
        return super.submit(task);
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        transactionMap.put(Thread.currentThread().getName(), runnableNameMap);
        super.beforeExecute(t, r);
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        String name = Thread.currentThread().getName();
        Map<String, String> runnableNameMap = transactionMap.get(name);
        if (t != null){
            runnableNameMap.putIfAbsent("flag", "ERROR");
            runnableNameMap.putIfAbsent("logError", t.toString());
        } else {
            runnableNameMap.putIfAbsent("flag", "SUCCESS");
        }
        runnableNameMap.put("end_time", System.currentTimeMillis()+"");
        transactionMap.put(name, runnableNameMap);
    }

    public void print(){
        System.out.println("任务详情");
        Set<Map.Entry<String, Map<String, String>>> entries = transactionMap.entrySet();
        for (Map.Entry<String, Map<String, String>> entry : entries) {
            Map<String, String> value = entry.getValue();
            System.out.println(JSONObject.toJSONString(value));
        }
    }
}
